using System;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using log4net;
using CodeMadeSimple.Xml;

namespace EnvRide.Core.ObjectModel
{
    /// <summary>
    /// This class represents an element override.
    /// </summary>
    public class ElementOverride : Override, IXmlSerializable
    {
        #region Fields

        private string _childElementXpath;
        private string _newXml;
        private ElementOverrideMode _overrideMode;

        #endregion

        #region Properties

        /// <summary>
        /// Override's mode.
        /// </summary>
        public ElementOverrideMode OverrideMode
        {
            get { return _overrideMode; }
            set { _overrideMode = value; }
        }

        /// <summary>
        /// Element's new xml.
        /// </summary>
        public string NewXml
        {
            get { return _newXml; }
            set { _newXml = value; }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Process the override.
        /// </summary>
        /// <param name="args">Processing arguments.</param>
        /// <param name="elem">Element found by the Xpath expression.</param>
        protected override void Process(EnvRideProcessArguments args, XmlElement elem)
        {
            switch (OverrideMode)
            {
                case ElementOverrideMode.AppendChild:

                    if (EnvRideProject.Logger.IsDebugEnabled)
                    {
                        EnvRideProject.Logger.DebugFormat("Appendind {0} to {1}.", NewXml, XmlUtils.GetFullPath(elem));
                    }

                    // Creates a new child
                    XmlDocumentFragment newChild = CreateFragmentFromNewXml(args);
                    elem.AppendChild(newChild);
                    break;

                case ElementOverrideMode.RemoveElement:

                    if (EnvRideProject.Logger.IsDebugEnabled)
                    {
                        EnvRideProject.Logger.DebugFormat("Removing {0}.", XmlUtils.GetFullPath(elem));
                    }

                    // Remove the element
                    elem.ParentNode.RemoveChild(elem);
                    break;

                case ElementOverrideMode.ReplaceElement:

                    if (EnvRideProject.Logger.IsDebugEnabled)
                    {
                        EnvRideProject.Logger.DebugFormat("Replacing {0} with {1}.", XmlUtils.GetFullPath(elem), NewXml);
                    }

                    // Replaces the element
                    XmlDocumentFragment replacementElement = CreateFragmentFromNewXml(args);
                    
                    XmlNode parentNode = elem.ParentNode;

                    parentNode.InsertAfter(replacementElement, elem);
                    parentNode.RemoveChild(elem);
                    break;
            }
        }

        /// <summary>
        /// Creates a XmlDocumentFragment with the content from the NewXml property.
        /// </summary>
        /// <param name="args">Process arguments.</param>
        /// <returns>XmlDocumentFragment with the content from the NewXml property.</returns>
        private XmlDocumentFragment CreateFragmentFromNewXml(EnvRideProcessArguments args)
        {
            if (NewXml == null)
                throw new ArgumentNullException("The new element Xml wasn't specified!");

            XmlDocumentFragment fragment = args.Document.CreateDocumentFragment();
            fragment.InnerXml = args.ParseValue(NewXml.Trim());
            return fragment;
        }

        #endregion

        #region IXmlSerializable Members

        public override void ReadXml(XmlReader reader)
        {
            base.ReadXml(reader);

            string overrideModeValue = reader.GetAttribute("overrideMode");

            if (!String.IsNullOrEmpty(overrideModeValue))
                OverrideMode = (ElementOverrideMode)Enum.Parse(typeof(ElementOverrideMode), overrideModeValue);

            if (!reader.IsEmptyElement)
            {
                NewXml = reader.ReadInnerXml().Replace(" xmlns=\"http://codeplex.envride.com/\"", String.Empty).Trim();
            }
            else
            {
                // Since the reader.ReadInnerXml advances to the next element, we have to advance too if there is no
                // element
                reader.Read();
            }
        }

        public override void WriteXml(XmlWriter writer)
        {
            base.WriteXml(writer);

            writer.WriteAttributeString("overrideMode", OverrideMode.ToString());
            writer.WriteRaw(NewXml);
        }

        #endregion

        protected override string GetDefaultName()
        {
            String defaultName = "ElementOverride";

            if (Environment != null && Environment.ElementOverrides != null)
            {
                defaultName += "[" + Environment.ElementOverrides.IndexOf(this) + "]";
            }

            return defaultName;
        }
    }
}